package com.xyd.auth.security.filter;

import com.alibaba.fastjson2.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xyd.common.result.GlobalResultClass;
import com.xyd.common.result.ResultStatusEnum;
import com.xyd.common.util.JwtHelper;
import com.xyd.common.util.ResponseUtil;
import com.xyd.model.system.vo.LoginVO;
import com.xyd.auth.security.custom.CustomUser;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
    // 注入redis
    private RedisTemplate redisTemplate;

    // 1.构造器
//构造方法中为RedisTemplate赋值
    public TokenLoginFilter(AuthenticationManager authenticationManager,RedisTemplate redisTemplate) {
        System.out.println("进入TokenLoginFilter");
        this.setAuthenticationManager(authenticationManager);
        this.setPostOnly(false);
        //指定登录接口及提交方式，可以指定任意路径
        this.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/admin/system/index/login", "POST")
        );
        // 传递redis，构造函数参数不要忘了引用
        this.redisTemplate = redisTemplate;
    }

    /**
     * 2 登录认证
     * 获取输入的用户名和密码 调用方法进行认证
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try{
            // 获取用户信息 (登录封装的实体类LoginVo)
            LoginVO loginVO = new ObjectMapper().readValue(request.getInputStream(), LoginVO.class);
            // 封装前端用户输入的账号密码对象
            Authentication authentication = new UsernamePasswordAuthenticationToken(loginVO.getUsername(), loginVO.getPassword());
            // 认证登录
            return this.getAuthenticationManager().authenticate(authentication);
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException(e);

        }
    }

    /**
     * 登录成功我们将权限数据保存到reids
     * 3 认证登录成功调用方法
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        // 获取当前用户对象
        CustomUser customUser = (CustomUser) authResult.getPrincipal();
        log.info("当前用户对象：{},{}",customUser, JSON.toJSONString(customUser.getAuthorities()));
        // redis中保存数据   key：用户名  value: 权限列表
        redisTemplate.opsForValue().set(customUser.getUsername(), JSON.toJSONString(customUser.getAuthorities()));


        // 生成token
        String token = JwtHelper.createToken(customUser.getSysUser().getId(), customUser.getSysUser().getUsername());
        log.info("创建令牌：{}",token);
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("token", token);
        //由于现在不是在控制层，无法使用RestController注解，只能使用原生方式返回,使用我们封装的工具类
        // 不要导错包 使我们自己定义的工具类
        ResponseUtil.out(response,GlobalResultClass.ok(map));
    }

    /**
     * 4.登录失败方法
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        if(failed instanceof RuntimeException){
            ResponseUtil.out(response,GlobalResultClass.build(null, ResultStatusEnum.DATA_ERROR));
        }
    }
}
